import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';

abstract class ArchiveAbleObject {
  Map<String, dynamic> toJson();

  ArchiveAbleObject toObject(Map<String, dynamic> json);
}

abstract class SynchronousData extends ChangeNotifier with ArchiveAbleObject {
  String? uuid;

  SynchronousData(this.uuid);

  /// 通讯Chanel初始化
  static const MethodChannel _channel = MethodChannel('synchronous_data');

  /// native 数据同步到 flutter
  void _nativeDataSyncToFlutter(Map<String, dynamic> arguments) {
    String uuid = arguments["uuid"];
    if (this.uuid == uuid) {
      try {
        Map<String, dynamic> data = Map<String, dynamic>.from(arguments["data"]);
        toObject(data);
      } catch (_) {}
      super.notifyListeners();
    }
  }

  /// 同步到native端数据结构
  @protected
  @mustCallSuper
  Future<dynamic> synchronousData(String uuid, {Map<String, dynamic>? dataMap}) async {
    assert(uuid.isNotEmpty);
    _ChanelMethodCallHandler._setChanelHandler(uuid, dataMap ?? <String, dynamic>{}, this);
    return _channel.invokeMethod("synchronousData", {
      "uuid": uuid,
      "data": dataMap ?? <String, dynamic>{},
    });
  }

  @override
  @protected
  @mustCallSuper
  Future<dynamic> notifyListeners() {
    super.notifyListeners();
    return _channel.invokeMethod("flutter_data_sync_to_native", {
      "uuid": uuid,
      "data": toJson(),
    });
  }

  @override
  void dispose() {
    _ChanelMethodCallHandler._removeHandler(uuid!);
    super.dispose();
  }
}

abstract class _ChanelMethodCallHandler {
  static bool hasSetCallHandler = false;
  static Map<String, Map<SynchronousData, Map<String, dynamic>>> syncDataObjects = <String, Map<SynchronousData, Map<String, dynamic>>>{};

  static void _removeHandler(String uuid) {
    syncDataObjects.removeWhere((key, value) => key == uuid);
  }

  static void _setChanelHandler(String uuid, Map<String, dynamic> data, SynchronousData object) {
    syncDataObjects[uuid] = <SynchronousData, Map<String, dynamic>>{object: data};
    if (hasSetCallHandler == true) return;
    SynchronousData._channel.setMethodCallHandler((call) async {
      Map<String, dynamic> arguments = Map<String, dynamic>.from(call.arguments ?? {});
      if (call.method == "native_data_sync_to_flutter") {
        Map<SynchronousData, Map<String, dynamic>>? objectMap = syncDataObjects[arguments["uuid"]];
        objectMap?.keys.first._nativeDataSyncToFlutter(arguments);
      }
      return true;
    });
    hasSetCallHandler = true;
  }
}
